package credis

import (
	"context"
	"time"

	"gitee.com/jieyang/go/clog"
	"gitee.com/jieyang/go/utils"

	"github.com/go-redsync/redsync/v4"
	"github.com/go-redsync/redsync/v4/redis/goredis/v9"
	"github.com/redis/go-redis/v9" //redis6=v8 redis7=v9
)

var Db *Redis

/*
[redis]
dbhost = "127.0.0.1"
dbport = 6379
dbpswd = ""
dbname = 0
dbprex = "test"
*/
func Init() {
	client := redis.NewClient(&redis.Options{
		Addr:     utils.EnvGet("redis", "dbhost").MustString("127.0.0.1") + ":" + utils.EnvGet("redis", "dbport").MustString("6379"),
		Password: utils.EnvGet("redis", "dbpswd").MustString(""), // no password set
		DB:       utils.EnvGet("redis", "dbname").MustInt(0),     // use default DB
	})
	ctx := context.Background()
	if err := client.Ping(ctx).Err(); err != nil {
		clog.Error().Str("redis链接失败", err.Error()).Send()
		return
	}
	Db = &Redis{
		ctx:  ctx,
		db:   client,
		prex: utils.EnvGet("redis", "dbprex").MustString(""),
	}
}

//redis官网推荐的go版本的分布式锁：redsync
//支持多节点
/*
// 通过相同的key值名获取同一个互斥锁.
	mutexname := "my-global-mutex"
	//创建基于key的互斥锁
	mutex := rs.NewMutex(mutexname)

	// 对key进行
	if err := mutex.Lock(); err != nil {
	    panic(err)
	}

	// 获取锁后的业务逻辑处理.

	// 释放互斥锁
	if ok, err := mutex.Unlock(); !ok || err != nil {
	    panic("unlock failed")
	}
*/
func NewRedsync() *redsync.Redsync {
	client := Db.db
	poolc := goredis.NewPool(client) //新建redsync客户端池
	return redsync.New(poolc)        //用客户端池建立锁 支持传入多个池
}

type Redis struct {
	ctx  context.Context
	db   *redis.Client
	prex string
}

func (r *Redis) TTl(key string) time.Duration {
	dt, err := r.db.TTL(r.ctx, r.prex+"_"+key).Result()
	if err != nil {
		clog.Error().Str("TTl获取过期时间", err.Error()).Str("key", key).Send()
		return 0
	}
	return dt
}

func Del(key string) {
	Db.Del(key)
}

func (r *Redis) Del(key string) {
	err := r.db.Del(r.ctx, r.prex+"_"+key).Err()
	if err != nil {
		clog.Error().Str("Del删除redis的key失败", err.Error()).Str("key", key).Send()
	}
}

// 同setEx存入 会替换原来的值 expiration为0表示永不过期 -1表示只改值不改时间 大于0为过期时间 默认为秒
func Set(key string, value interface{}, expiration time.Duration) {
	Db.Set(key, value, expiration)
}

func (r *Redis) Set(key string, value interface{}, expiration time.Duration) {
	if r == nil {
		return
	}
	if expiration == -1 {
		expiration = redis.KeepTTL
	}
	err := r.db.Set(r.ctx, r.prex+"_"+key, value, expiration).Err()
	if err != nil {
		clog.Error().Str("Set存入redis的key失败", err.Error()).Str("key", key).Send()
	}
}

// setNx存入 如果不存在才写入 不会替换
func (r *Redis) SetNx(key string, value interface{}, expiration time.Duration) (bool, error) {
	return r.db.SetNX(r.ctx, r.prex+"_"+key, value, expiration).Result()
}

// HSet存入
func (r *Redis) HSet(key string, values ...interface{}) {
	err := r.db.HSet(r.ctx, r.prex+"_"+key, values...).Err()
	if err != nil {
		clog.Error().Str("HSet存入redis的key失败", err.Error()).Str("key", key).Send()
	}
}

func (r *Redis) Expire(key string, expiration time.Duration) {
	if expiration == -1 {
		expiration = redis.KeepTTL
	}
	err := r.db.Expire(r.ctx, r.prex+"_"+key, expiration).Err()
	if err != nil {
		clog.Error().Str("Expire存入redis的key", err.Error()).Str("key", key).Send()
	}
}

// HGet
func (r *Redis) HGet(key string, field string) (string, bool) {
	val, err := r.db.HGet(r.ctx, r.prex+"_"+key, field).Result()
	if err != nil {
		return "", false
	}
	return val, true
}

// HGetAll
func (r *Redis) HGetAll(key string) (map[string]string, bool) {
	val, err := r.db.HGetAll(r.ctx, r.prex+"_"+key).Result()
	if err != nil {
		return nil, false
	}
	if len(val) == 0 {
		return nil, false
	}
	return val, true
}

// get直接返回string
func Get(key string) (string, bool) {
	return Db.Get(key)
}

func (r *Redis) Get(key string) (string, bool) {
	val, err := r.db.Get(r.ctx, r.prex+"_"+key).Result()
	if err != nil {
		return "", false
	}
	return val, true
}

// 返回[]byte
func (r *Redis) GetBytes(key string) ([]byte, bool) {
	if r == nil {
		return nil, false
	}
	val, err := r.db.Get(r.ctx, r.prex+"_"+key).Bytes()
	if err != nil {
		return nil, false
	}
	return val, true
}

// 自写 直接返回uint
func (r *Redis) GetUint(key string) (uint, bool) {
	val, err := r.db.Get(r.ctx, r.prex+"_"+key).Uint64()
	if err != nil {
		return 0, false
	}
	return uint(val), true
}

func Incr(key string) int64 {
	Db.Incr(key)
	return GetInt64(key)
}

func (r *Redis) Incr(key string) {
	r.db.Incr(r.ctx, r.prex+"_"+key)
}

func (r *Redis) IncrBy(key string, value int64) {
	r.db.IncrBy(r.ctx, r.prex+"_"+key, value)
}

func Decr(key string) int64 {
	Db.Decr(key)
	return GetInt64(key)
}

func (r *Redis) Decr(key string) {
	r.db.Decr(r.ctx, r.prex+"_"+key)
}

func (r *Redis) DecrBy(key string, value int64) {
	r.db.DecrBy(r.ctx, r.prex+"_"+key, value)
}

func GetInt(key string) int {
	if val, ok := Db.GetInt(key); ok {
		return val
	}
	return 0
}

// 自写 直接返回int
func (r *Redis) GetInt(key string) (int, bool) {
	val, err := r.db.Get(r.ctx, r.prex+"_"+key).Int64()
	if err != nil {
		return 0, false
	}
	return int(val), true
}

func GetInt64(key string) int64 {
	if val, ok := Db.GetInt64(key); ok {
		return val
	}
	return 0
}

// 自写 直接返回int64
func (r *Redis) GetInt64(key string) (int64, bool) {
	val, err := r.db.Get(r.ctx, r.prex+"_"+key).Int64()
	if err != nil {
		return 0, false
	}
	return val, true
}

// 自写 直接返回float64
func (r *Redis) GetFloat64(key string) (float64, bool) {
	val, err := r.db.Get(r.ctx, r.prex+"_"+key).Float64()
	if err != nil {
		return 0, false
	}
	return val, true
}

//geo

// geoadd
func GeoAdd(key string, geoLocation *redis.GeoLocation) {
	Db.GeoAdd(key, geoLocation)
}

func (r *Redis) GeoAdd(key string, geoLocation *redis.GeoLocation) {
	r.db.GeoAdd(r.ctx, r.prex+"_"+key, geoLocation)
}

// georadius
func GeoRadius(key string, longitude, latitude float64, query *redis.GeoRadiusQuery) []redis.GeoLocation {
	return Db.GeoRadius(key, longitude, latitude, query)
}

func (r *Redis) GeoRadius(key string, longitude, latitude float64, query *redis.GeoRadiusQuery) []redis.GeoLocation {
	res, err := r.db.GeoRadius(r.ctx, r.prex+"_"+key, longitude, latitude, query).Result()
	if err != nil {
		clog.Error().Str("GeoRadius获取redis的key失败", err.Error()).Str("key", key).Send()
		return nil
	}
	return res
}

// 删除
func GeoDelete(key string, member string) {
	Db.GeoDelete(key, member)
}

func (r *Redis) GeoDelete(key string, member string) {
	r.db.ZRem(r.ctx, r.prex+"_"+key, member)
}
